"""
配置管理器
负责从数据库加载配置，为数据清洗处理器提供配置数据
"""
import re
import logging
from typing import List, Dict, Set, Optional, Tuple, Callable
from sqlalchemy.orm import Session

from backend.data_clean.models import (
    BrandConfig, CommonProductName, UnitConfig, 
    MarketingWordConfig, SystemConfig, UnitConversion
)

# 设置日志
logger = logging.getLogger(__name__)

class ConfigManager:
    """配置管理器"""
    
    def __init__(self):
        self._brands: List[str] = []
        self._brand_dict: Dict[str, Dict] = {}
        self._common_products: List[str] = []
        self._common_product_dict: Dict[str, Dict] = {}
        self._units: List[str] = []
        self._unit_dict: Dict[str, Dict] = {}
        self._marketing_words: List[str] = []
        self._marketing_word_dict: Dict[str, Dict] = {}
        self._system_config: Dict[str, str] = {}
        
        # 单位转换相关
        self._unit_conversions: List[Dict] = []
        
        # 默认配置 - 仅作为初始值，将在加载数据库配置时覆盖
        self._specification_pattern = r'(\d+\.?\d*\s*(?:{units})|约\s*\d+\.?\d*\s*(?:{units}))'
        self._brand_char_range = r'[\u4e00-\u9fa5]'
        self._brand_max_length = 6
        
        # 单位转换配置
        self._unit_conversion: Dict[str, Callable[[float], float]] = {}
        self._standard_unit_map: Dict[str, str] = {}
        
        # 清理配置
        self._clean_chars_pattern = r'[\s/\\-]+'
        
        self._is_initialized = False
    
    def initialize(self, db: Optional[Session] = None):
        """初始化配置，从数据库加载配置项
        
        Args:
            db: 数据库会话
        """
        if hasattr(self, '_is_initialized') and self._is_initialized:
            logger.debug("配置管理器已初始化，跳过重复初始化")
            return
        
        logger.info("初始化配置管理器")
        
        # 清空现有配置
        self._brands = []
        self._units = []
        self._marketing_words = []
        self._common_products = []
        self._system_config = {}
        
        # 加载配置
        if db is not None:
            # 加载各项配置
            self._load_brands(db)
            self._load_common_products(db)
            self._load_units(db)
            self._load_marketing_words(db)
            self._load_system_config(db)
            self._load_unit_conversions(db)
            self._init_unit_conversion()
        else:
            # 使用默认配置
            logger.warning("未提供数据库会话，将使用默认配置")
        
        # 编译正则表达式模式
        if hasattr(self, '_compile_patterns'):
            self._compile_patterns()
        
        self._is_initialized = True
        logger.info("配置管理器初始化完成")
    
    def _load_unit_conversions(self, db: Session):
        """加载单位转换规则"""
        try:
            conversion_configs = db.query(UnitConversion).filter(UnitConversion.is_active == True).all()
            self._unit_conversions = [
                {
                    "id": conv.id,
                    "source_unit": conv.source_unit,
                    "target_unit": conv.target_unit,
                    "conversion_formula": conv.conversion_formula,
                    "description": conv.description,
                    "is_active": conv.is_active
                }
                for conv in conversion_configs
            ]
            logger.info(f"加载了 {len(self._unit_conversions)} 个单位转换规则")
        except Exception as e:
            logger.error(f"加载单位转换规则失败: {str(e)}")
            # 使用默认的空列表
    
    def _init_unit_conversion(self):
        """初始化单位转换规则"""
        # 清空之前的配置，确保所有配置都来自数据库
        self._unit_conversion = {}
        self._standard_unit_map = {}
        
        # 加载数据库中的单位转换规则
        for conv in self._unit_conversions:
            try:
                source_unit = conv["source_unit"]
                formula = conv["conversion_formula"]
                target_unit = conv["target_unit"]
                
                # 创建转换函数
                self._unit_conversion[source_unit] = eval(f"lambda x: {formula}")
                # 设置标准单位映射
                self._standard_unit_map[source_unit] = target_unit
                
                logger.info(f"从数据库加载单位转换规则: {source_unit} -> {target_unit} (公式: {formula})")
            except Exception as e:
                logger.warning(f"无法解析转换规则 {conv['source_unit']} -> {conv['target_unit']}: {str(e)}")
        
        # 如果数据库中没有配置，添加几个默认的转换规则
        if not self._unit_conversion:
            logger.warning("数据库中没有单位转换规则配置，添加默认转换规则")
            # 添加克(g)到千克(kg)的转换：1000克 = 1千克
            self._unit_conversion["g"] = lambda x: x / 1000.0
            self._standard_unit_map["g"] = "kg"
            
            # 添加克(g)到斤的转换：500克 = 1斤
            self._unit_conversion["克"] = lambda x: x / 500.0
            self._standard_unit_map["克"] = "斤"
            
            # 添加千克(kg)到斤的转换：1千克 = 2斤
            self._unit_conversion["kg"] = lambda x: x * 2
            self._standard_unit_map["kg"] = "斤"
            
            # 添加千克(kg)到斤的转换：1千克 = 2斤
            self._unit_conversion["千克"] = lambda x: x * 2
            self._standard_unit_map["千克"] = "斤"
            
            logger.info(f"添加了默认单位转换规则: g->kg, 克->斤, kg->斤, 千克->斤")
        
        # 加载系统配置中的单位转换规则
        if 'UNIT_CONVERSION' in self._system_config:
            try:
                custom_conversions = eval(self._system_config['UNIT_CONVERSION'])
                if isinstance(custom_conversions, dict):
                    for unit, formula_str in custom_conversions.items():
                        # 将字符串公式转换为lambda函数
                        self._unit_conversion[unit] = eval(f'lambda x: {formula_str}')
                    logger.info(f"从系统配置加载自定义单位转换规则: {list(custom_conversions.keys())}")
            except Exception as e:
                logger.error(f"加载自定义单位转换规则失败: {str(e)}")
        
        # 加载系统配置中的标准单位映射
        if 'STANDARD_UNIT_MAP' in self._system_config:
            try:
                custom_unit_map = eval(self._system_config['STANDARD_UNIT_MAP'])
                if isinstance(custom_unit_map, dict):
                    self._standard_unit_map.update(custom_unit_map)
                    logger.info(f"从系统配置加载自定义标准单位映射: {list(custom_unit_map.keys())}")
            except Exception as e:
                logger.error(f"加载自定义标准单位映射失败: {str(e)}")
        
        # 记录最终的单位转换配置
        logger.info(f"最终单位转换规则: {list(self._unit_conversion.keys())}")
        logger.info(f"最终标准单位映射: {self._standard_unit_map}")

    def _load_brands(self, db: Session):
        """加载品牌配置"""
        try:
            # 确保数据库会话有效
            if db is None or not db.is_active:
                logger.error("数据库会话无效，尝试使用备用方法加载品牌")
                # 尝试从配置中直接创建一个新的会话
                from backend.database.session import get_db
                # 这里会创建一个新的会话
                db_gen = get_db()
                db = next(db_gen)
                logger.info("成功创建新的数据库会话")

            # 记录SQL查询
            logger.info("执行品牌查询: SELECT * FROM brand_config WHERE is_active = True")
            
            # 确保BrandConfig类已正确导入
            from backend.data_clean.models import BrandConfig
            
            # 进行查询
            brand_configs = db.query(BrandConfig).filter(BrandConfig.is_active == True).all()
            
            # 记录查询结果
            logger.info(f"查询返回了 {len(brand_configs)} 条品牌记录")
            
            self._brands = [brand.name for brand in brand_configs]
            self._brand_dict = {brand.name: {
                "id": brand.id,
                "category": brand.category,
                "is_active": brand.is_active
            } for brand in brand_configs}
            
            logger.info(f"从数据库加载了 {len(self._brands)} 个品牌配置")
            
            # 记录前10个品牌名称作为示例
            if self._brands:
                sample_brands = self._brands[:min(10, len(self._brands))]
                logger.info(f"品牌样例: {', '.join(sample_brands)}")
            else:
                # 如果品牌列表为空，检查数据库中是否存在品牌表
                logger.warning("数据库中没有找到任何品牌配置，尝试检查品牌表是否存在")
                try:
                    # 检查表是否存在
                    check_table = db.execute("SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'brand_config')")
                    table_exists = check_table.scalar()
                    logger.info(f"品牌表存在状态: {table_exists}")
                    
                    if table_exists:
                        # 表存在但没有数据，尝试插入一些测试数据
                        logger.info("尝试插入测试品牌数据")
                        test_brands = ["测试品牌1", "测试品牌2", "测试品牌3"]
                        for i, name in enumerate(test_brands):
                            new_brand = BrandConfig(
                                name=name,
                                category="测试类别",
                                is_active=True
                            )
                            db.add(new_brand)
                        db.commit()
                        logger.info("成功插入测试品牌数据")
                        
                        # 重新查询
                        brand_configs = db.query(BrandConfig).filter(BrandConfig.is_active == True).all()
                        self._brands = [brand.name for brand in brand_configs]
                        self._brand_dict = {brand.name: {
                            "id": brand.id,
                            "category": brand.category,
                            "is_active": brand.is_active
                        } for brand in brand_configs}
                        logger.info(f"重新加载后品牌数量: {len(self._brands)}")
                except Exception as check_error:
                    logger.error(f"检查品牌表失败: {str(check_error)}")
        except Exception as e:
            logger.error(f"从数据库加载品牌配置失败: {str(e)}")
            import traceback
            logger.error(f"异常详情: {traceback.format_exc()}")
            # 创建一些默认品牌以便测试
            self._brands = ["默认品牌1", "默认品牌2", "默认品牌3"]
            self._brand_dict = {brand: {"id": i, "category": "默认类别", "is_active": True} 
                              for i, brand in enumerate(self._brands)}
            logger.warning(f"由于加载失败，创建了 {len(self._brands)} 个默认品牌以便测试。")
            logger.info(f"默认品牌: {', '.join(self._brands)}")
    
    def _load_common_products(self, db: Session):
        """加载商品名称排除列表"""
        try:
            product_configs = db.query(CommonProductName).filter(CommonProductName.is_active == True).all()
            self._common_products = [product.name for product in product_configs]
            self._common_product_dict = {product.name: {
                "id": product.id,
                "category": product.category,
                "description": product.description,
                "is_active": product.is_active
            } for product in product_configs}
            logger.info(f"从数据库加载了 {len(self._common_products)} 个商品名称排除项")
            if not self._common_products:
                logger.warning("数据库中没有找到任何商品名称排除配置，请确保已经正确导入商品数据")
        except Exception as e:
            logger.error(f"从数据库加载商品名称排除列表失败: {str(e)}")
            self._common_products = []
            self._common_product_dict = {}
            logger.warning("由于加载失败，将使用空的商品名称排除列表。请修复数据库连接并重新加载配置。")
    
    def _load_units(self, db: Session):
        """加载单位配置"""
        try:
            unit_configs = db.query(UnitConfig).filter(UnitConfig.is_active == True).all()
            self._units = [unit.name for unit in unit_configs]
            self._unit_dict = {unit.name: {
                "id": unit.id,
                "type": unit.type,
                "is_active": unit.is_active
            } for unit in unit_configs}
            logger.info(f"从数据库加载了 {len(self._units)} 个单位配置")
            if not self._units:
                logger.warning("数据库中没有找到任何单位配置，请确保已经正确导入单位数据")
        except Exception as e:
            logger.error(f"从数据库加载单位配置失败: {str(e)}")
            self._units = []
            self._unit_dict = {}
            logger.warning("由于加载失败，将使用空的单位列表。请修复数据库连接并重新加载配置。")
    
    def _load_marketing_words(self, db: Session):
        """加载营销词配置"""
        try:
            word_configs = db.query(MarketingWordConfig).filter(MarketingWordConfig.is_active == True).all()
            self._marketing_words = [word.word for word in word_configs]
            self._marketing_word_dict = {word.word: {
                "id": word.id,
                "category": word.category,
                "is_active": word.is_active
            } for word in word_configs}
            logger.info(f"从数据库加载了 {len(self._marketing_words)} 个营销词配置")
            if not self._marketing_words:
                logger.warning("数据库中没有找到任何营销词配置，请确保已经正确导入营销词数据")
        except Exception as e:
            logger.error(f"从数据库加载营销词配置失败: {str(e)}")
            self._marketing_words = []
            self._marketing_word_dict = {}
            logger.warning("由于加载失败，将使用空的营销词列表。请修复数据库连接并重新加载配置。")
    
    def _load_system_config(self, db: Session):
        """加载系统配置"""
        try:
            system_configs = db.query(SystemConfig).all()
            self._system_config = {config.key: config.value for config in system_configs}
            logger.info(f"从数据库加载了 {len(self._system_config)} 个系统配置")
            
            # 设置规格匹配模式
            if "SPECIFICATION_PATTERN" in self._system_config:
                self._specification_pattern = self._system_config["SPECIFICATION_PATTERN"]
                logger.info(f"使用数据库配置的规格匹配模式: {self._specification_pattern}")
            else:
                logger.warning("数据库中未找到规格匹配模式配置(SPECIFICATION_PATTERN)，请确保已经正确导入系统配置")
            
            # 设置品牌字符范围
            if "BRAND_CHAR_RANGE" in self._system_config:
                self._brand_char_range = self._system_config["BRAND_CHAR_RANGE"]
                logger.info(f"使用数据库配置的品牌字符范围: {self._brand_char_range}")
            else:
                logger.warning("数据库中未找到品牌字符范围配置(BRAND_CHAR_RANGE)，请确保已经正确导入系统配置")
            
            # 设置品牌最大长度
            if "BRAND_MAX_LENGTH" in self._system_config:
                try:
                    self._brand_max_length = int(self._system_config["BRAND_MAX_LENGTH"])
                    logger.info(f"使用数据库配置的品牌最大长度: {self._brand_max_length}")
                except (ValueError, TypeError):
                    logger.warning("品牌最大长度解析失败，请确保BRAND_MAX_LENGTH是一个有效的整数")
            else:
                logger.warning("数据库中未找到品牌最大长度配置(BRAND_MAX_LENGTH)，请确保已经正确导入系统配置")
            
            # 设置清理字符模式
            if "CLEAN_CHARS_PATTERN" in self._system_config:
                self._clean_chars_pattern = self._system_config["CLEAN_CHARS_PATTERN"]
                logger.info(f"使用数据库配置的清理字符模式: {self._clean_chars_pattern}")
            else:
                logger.warning("数据库中未找到清理字符模式配置(CLEAN_CHARS_PATTERN)，请确保已经正确导入系统配置")
                
            if not self._system_config:
                logger.warning("数据库中没有找到任何系统配置，请确保已经正确导入系统配置数据")
        except Exception as e:
            logger.error(f"从数据库加载系统配置失败: {str(e)}")
            self._system_config = {}
            logger.warning("由于加载失败，将使用默认系统配置。请修复数据库连接并重新加载配置。")
    
    def reload(self, db: Session):
        """重新加载配置"""
        self.initialize(db)
    
    @property
    def brands(self) -> List[str]:
        """获取品牌列表"""
        return self._brands
    
    @property
    def brand_dict(self) -> Dict[str, Dict]:
        """获取品牌字典"""
        return self._brand_dict
    
    def is_brand(self, name: str) -> bool:
        """检查是否是品牌"""
        return name in self._brands
    
    @property
    def common_products(self) -> List[str]:
        """获取商品名称排除列表"""
        return self._common_products
    
    @property
    def common_product_dict(self) -> Dict[str, Dict]:
        """获取商品名称排除字典"""
        return self._common_product_dict
    
    def is_common_product(self, name: str) -> bool:
        """检查是否是常见商品名称"""
        return name in self._common_products
    
    @property
    def units(self) -> List[str]:
        """获取单位列表"""
        return self._units
    
    @property
    def unit_dict(self) -> Dict[str, Dict]:
        """获取单位字典"""
        return self._unit_dict
    
    def is_unit(self, name: str) -> bool:
        """检查是否是单位"""
        return name in self._units
    
    @property
    def marketing_words(self) -> List[str]:
        """获取营销词列表"""
        return self._marketing_words
    
    @property
    def marketing_word_dict(self) -> Dict[str, Dict]:
        """获取营销词字典"""
        return self._marketing_word_dict
    
    def is_marketing_word(self, word: str) -> bool:
        """检查是否是营销词"""
        return word in self._marketing_words
    
    @property
    def specification_pattern(self) -> str:
        """获取规格匹配模式"""
        return self._specification_pattern
    
    @property
    def brand_char_range(self) -> str:
        """获取品牌字符范围"""
        return self._brand_char_range
    
    @property
    def brand_max_length(self) -> int:
        """获取品牌最大长度"""
        return self._brand_max_length
    
    @property
    def clean_chars_pattern(self) -> str:
        """获取清理字符的正则表达式模式"""
        return self._clean_chars_pattern
    
    @property
    def unit_conversion(self) -> Dict[str, Callable[[float], float]]:
        """获取单位转换规则"""
        return self._unit_conversion
    
    @property
    def standard_unit_map(self) -> Dict[str, str]:
        """获取标准单位映射"""
        return self._standard_unit_map
    
    def get_brand_pattern(self) -> str:
        """获取品牌匹配模式"""
        return f'^{self._brand_char_range}{{2,{self._brand_max_length}}}'
    
    def get_specification_pattern_with_units(self) -> str:
        """获取带单位的规格匹配模式"""
        if not self._units:
            return r'\d+\.?\d*\s*(?:个|件|包|袋|箱|瓶|罐|盒|片|克|g|kg|千克|升|ml|L)'
        
        return self._specification_pattern.format(units='|'.join(self._units))
    
    def convert_to_standard_unit(self, value: float, unit: str) -> Tuple[float, str]:
        """
        将给定的值和单位转换为标准单位
        
        Args:
            value: 数值
            unit: 单位
            
        Returns:
            转换后的数值和标准单位
        """
        if unit in self._unit_conversion and unit in self._standard_unit_map:
            try:
                converted_value = self._unit_conversion[unit](value)
                standard_unit = self._standard_unit_map[unit]
                return converted_value, standard_unit
            except Exception as e:
                logger.warning(f"单位转换失败: {str(e)}")
        
        # 如果转换失败，返回原始值和单位
        return value, unit
    
    def clean_product_name(self, name: str) -> str:
        """
        清理商品名称，去除多余字符
        
        Args:
            name: 原始商品名称
            
        Returns:
            清理后的商品名称
        """
        # 去除多余的空格、斜杠、反斜杠、短横线等
        clean_name = re.sub(self._clean_chars_pattern, ' ', name).strip()
        # 标准化空格（多个空格合并为一个）
        clean_name = ' '.join(clean_name.split())
        return clean_name
    
    def get_unknown_brand(self, brand: str) -> Dict:
        """获取未知品牌信息"""
        return {
            "id": None,
            "name": brand,
            "category": "未知",
            "is_in_database": False
        }
    
    def get_brand_info(self, brand: str) -> Dict:
        """获取品牌信息"""
        if brand in self._brand_dict:
            return {
                "id": self._brand_dict[brand]["id"],
                "name": brand,
                "category": self._brand_dict[brand]["category"],
                "is_in_database": True
            }
        return self.get_unknown_brand(brand)

    def _compile_patterns(self):
        """编译常用的正则表达式模式"""
        try:
            # 仅编译，不实际使用，确保模式有效
            if self._specification_pattern:
                if self._units:
                    re.compile(self._specification_pattern.format(units='|'.join(self._units)))
                else:
                    re.compile(self._specification_pattern.format(units='个|件|包|袋|箱|瓶|罐|盒|片|克|g|kg|千克|升|ml|L'))
                logger.info("规格匹配模式编译成功")
            
            if self._brand_char_range:
                re.compile(f'^{self._brand_char_range}{{2,{self._brand_max_length}}}')
                logger.info("品牌匹配模式编译成功")
                
            if self._clean_chars_pattern:
                re.compile(self._clean_chars_pattern)
                logger.info("清理字符模式编译成功")
        except Exception as e:
            logger.error(f"正则表达式模式编译失败: {str(e)}")
            # 使用默认的安全模式
            self._specification_pattern = r'(\d+\.?\d*\s*(?:{units})|约\s*\d+\.?\d*\s*(?:{units}))'
            self._brand_char_range = r'[\u4e00-\u9fa5]'
            self._clean_chars_pattern = r'[\s/\\-]+'
            logger.warning("使用默认的正则表达式模式")

# 创建全局配置管理器实例
config_manager = ConfigManager() 